package com.quectel.pay.module.order.service.impl;

import cn.hutool.core.util.ReflectUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.quectel.constant.global.SystemConstants;
import com.quectel.pay.module.merchant.service.PayChannelService;
import com.quectel.pay.module.order.dto.PayOrderDto;
import com.quectel.pay.module.order.dto.PayOrderRefundDto;
import com.quectel.pay.module.order.dto.request.InitOrderParams;
import com.quectel.pay.module.order.dto.response.NotifyCheckResult;
import com.quectel.pay.module.order.dto.response.OrderCreatedResponseDto;
import com.quectel.pay.module.order.dto.response.SendRefundResp;
import com.quectel.pay.module.order.service.OrderAliJSAPIPayService;
import com.quectel.pay.module.order.service.PayOrderService;
import com.quectel.pay.util.PayInstanceUtils;
import com.quectel.util.common.JacksonUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付宝JSAPI支付
 *
 * @author: caolu
 * @email: louis.cao@quectel.com
 * @date: 2023-04-13 14:03:00
 */
@DubboService
public class OrderAliJSAPIPayServiceImpl implements OrderAliJSAPIPayService {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderAliJSAPIPayServiceImpl.class);

    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private PayChannelService payChannelService;
    @Autowired
    private OrderAliJSAPIPayService orderAliJSAPIPayService;

    @Override
    public OrderCreatedResponseDto createOrder(InitOrderParams initOrderParams) {

        LOGGER.info("Ali JSAPI-Pay create order params are: {}", JacksonUtils.toJsonString(initOrderParams));

        //初始化订单
        initOrderParams.setPayType(SystemConstants.PayType.ZFB.getType());

        PayOrderDto orderDto = payOrderService.initOrderInfo(initOrderParams);
        orderDto.setTerminalType(SystemConstants.TerminalType.JSAPI.getType());


        //创建API对应的request
        AlipayTradeCreateRequest alipayRequest = new AlipayTradeCreateRequest();
        //在公共参数中设置回跳和通知地址
        alipayRequest.setNotifyUrl(orderDto.getNotifyUrl());

        Map<String, Object> bizParams = new HashMap<>();
        bizParams.put("out_trade_no", orderDto.getOutTradeNo());
        bizParams.put("total_amount", orderDto.getSpecialPrice());
        bizParams.put("subject", orderDto.getProductName());
        bizParams.put("buyer_id", orderDto.getOpenId());
        if (initOrderParams.getTtl() != null && initOrderParams.getTtl() > 0) {
            //分钟
            bizParams.put("timeout_express", initOrderParams.getTtl() / 1000 / 60 + "m");
        }
        alipayRequest.setBizContent(JacksonUtils.toJsonString(bizParams));//填充业务参数
        OrderCreatedResponseDto orderCreatedResponseDto = new OrderCreatedResponseDto();
        try {
            //调用SDK生成表单
            AlipayTradeCreateResponse response =
                    PayInstanceUtils.getAliInstance(initOrderParams.getPayChannelId()).execute(alipayRequest);
            if (response.isSuccess()) {
                orderCreatedResponseDto.setSuccess(true);
                Map<String, String> zfbMap = new HashMap<>();
                zfbMap.put("tradeNo", response.getTradeNo());
                orderCreatedResponseDto.setResp(zfbMap);
                orderCreatedResponseDto.setOutTradeNo(orderDto.getOutTradeNo());

                orderDto.setTradeNo(response.getTradeNo());
                orderDto.setRedoValue(JacksonUtils.toJsonString(orderCreatedResponseDto));
                payOrderService.save(orderDto);

                LOGGER.debug("返回的数据为:{}", JacksonUtils.toJsonString(orderCreatedResponseDto));

                return orderCreatedResponseDto;
            }
        } catch (AlipayApiException e) {
            LOGGER.error("Ali JSAPI-Pay create order failed", e);
        }
        return null;
    }

    @Override
    public SendRefundResp sendRefundRequest(Long payChannelId, PayOrderRefundDto refundReq) {
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        Map<String, Object> params = new HashMap<>();
        params.put("out_trade_no", refundReq.getOutTradeNo());
        params.put("refund_amount", refundReq.getRefundAmount());
        params.put("out_request_no", refundReq.getOutRefundNo());
        request.setBizContent(JacksonUtils.toJsonString(params));
        try {
            AlipayTradeRefundResponse response = PayInstanceUtils.getAliInstance(payChannelId).execute(request);

            LOGGER.info("Ali refund response daya：{}", JacksonUtils.toJsonString(response));

            if (response.isSuccess() && response.getGmtRefundPay() != null) {
                LOGGER.info("Ali refund success, the outTradeNo is: {} outRefundNo is: {} ", refundReq.getOutTradeNo(), refundReq.getOutRefundNo());
                return SendRefundResp
                        .builder()
                        .sendSuccess(true)
                        .refundWithSynchronization(true)
                        .refundSuccessTime(response.getGmtRefundPay())
                        .build();
            }
            //失败的订单不需要存入
            LOGGER.info("Ali refund failed, the outTradeNo is: {} error msg: {}", refundReq.getOutTradeNo(), response.getBody());
            return SendRefundResp
                    .builder()
                    .sendSuccess(false)
                    .refundWithSynchronization(true)
                    .failedReason(response.getSubMsg() != null ? response.getSubMsg() : response.getMsg())
                    .build();

        } catch (AlipayApiException e) {
            LOGGER.error("Ali send refund request failed", e);
        }
        return SendRefundResp.builder().sendSuccess(false).failedReason("未知原因").build();
    }

    @Override
    public NotifyCheckResult checkNotify(Long payChannelId, Map<String, String[]> notifyParams) {
        Map<String, String> params = new HashMap<>();
        //获取支付宝POST过来反馈信息
        for (Map.Entry<String, String[]> entry : notifyParams.entrySet()) {
            params.put(entry.getKey(), StringUtils.join(entry.getValue(), ','));
        }

        LOGGER.info("Ali pay(or refund) notify params are: {}", JacksonUtils.toJsonString(params));

        NotifyCheckResult notifyCheckResult = new NotifyCheckResult();
        try {
            DefaultAlipayClient aliInstance = PayInstanceUtils.getAliInstance(payChannelId);
            String alipayPublicKey = (String) ReflectUtil.getFieldValue(aliInstance, "alipayPublicKey");

            boolean flag = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");
            if (flag) {
                notifyCheckResult.setCheckSuccess(true);
                notifyCheckResult.setOutTradeNo(params.get("out_trade_no"));
                notifyCheckResult.setTradeNo(params.get("trade_no"));

                if (StringUtils.isNotBlank(params.get("refund_fee"))) {
                    //退款类型 支付宝发起后就知道是否可以退款成功 不需要回调这里是多余的
                    //这里有个坑  10元分10次退每次退1元 前9笔有回调后期 最后一笔是交易关闭不触发异步通知(即全额退款 所以我们要用同步接口去判断退款成功不依赖异步)
                    /**
                     * 在这个退款接口是没有传入异步地址的位置的，退款的异步通知是依据支付接口的触发条件来触发的，异步通知也是发送到支付接口传入的异步地址上。
                     *
                     *   （1）部分退款：部分退款交易状态是处于TRADE_SUCCESS（交易成功），此时因部分退款导致交易金额变动，都会触发异步通知
                     *   （2）全额退款：交易成功后全额退款，交易状态会转为TRADE_CLOSED（交易关闭），此时根据不同的支付接口触发条件也不同，
                     *   例如APP支付接口TRADE_CLOSED（交易关闭）状态触发异步，此时就会收到全额退款的异步通知。而电脑网站支付TRADE_CLOSED（交易关闭）状态不会触发异步，就不会有全额退款的异步通知
                     */
                    notifyCheckResult.setTradeStatusType(Byte.valueOf("2"));
                    if ("TRADE_SUCCESS".equals(params.get("trade_status"))) {
                        //如果有这个代表退款
                        notifyCheckResult.setRefundSuccess(true);
                        notifyCheckResult.setOutRefundNo(params.get("out_biz_no"));
                        notifyCheckResult.setRefundSuccessTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S").parse(params.get("gmt_refund")));
                    } else {
                        //退款失败原因
                        notifyCheckResult.setFailReason(params.get("trade_status") + ":退款失败");
                    }
                } else {
                    //支付类型
                    notifyCheckResult.setTradeStatusType(Byte.valueOf("1"));
                    if ("TRADE_SUCCESS".equals(params.get("trade_status"))) {
                        notifyCheckResult.setPaySuccess(true);
                        notifyCheckResult.setPaySuccessTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(params.get("gmt_payment")));
                    } else {
                        //支付失败原因
                        notifyCheckResult.setFailReason(params.get("trade_status") + ":支付失败");
                    }
                }
            }
            return notifyCheckResult;
        } catch (Exception e) {
            LOGGER.error("Ali pay(or refund) notify params check error");
        }
        return notifyCheckResult;
    }
}
